﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.Extensions.Options;
using SqlSugar;
using System.Text;
using OneCard.Face8280Module.Entities.DTO.Cache;
using OneCard.Face8280Module.DeviceModel.DTO.People.Request;
using OneCard.Face8280Module.DeviceModel.DTO;
using OneCard.Face8280Module.DeviceModel.DTO.Keepalive;
using OneCard.Face8280Module.DeviceModel.DTO.People.Result;
using OneCard.Face8280Module.DeviceModel.Model;
using OneCard.Face8280Module.DeviceModel.DTO.WorkParameter;
using OneCard.Face8280Module.Service;
using OneCard.Face8280Module.Utility;
using OneCard.Face8280Module.Config;
using System.IO.Compression;

namespace OneCard.Face8280Module.Controllers
{
    [ApiController]
    //[ServiceFilter(typeof(FaceAPILogResourceFilter))]
    public class FaceAPIController : ControllerBase
    {
        private static object lockobject = new object();
        private DataService DBService;
        private CacheService Cache;
        private ILogger<FaceAPIController> Logger;

        public FaceAPIController(DataService dbClient, CacheService cache, ILogger<FaceAPIController> logger)
        {
            DBService = dbClient;
            Cache = cache;
            Logger = logger;
        }

        #region 打卡记录
        /// <summary>
        /// 设备主动往云端实时上传打卡记录
        /// </summary>
        /// <param name="record"></param>
        /// <returns></returns>
        [HttpPost, Route("/note/insertNoteFace")]
        [ServiceFilter(typeof(ReplayAttackFillter))]
        public async Task<FaceAPIResult> UploadRecord([FromForm] string recordJson, [FromForm] IFormCollection files)
        {
            if (string.IsNullOrEmpty(recordJson))
            {
                //没有找到recordJson，可能是gzip压缩了
                var ifile = files.Files.GetFile("recordJson");

                if (ifile != null)
                {
                    if (ifile.Headers.ContainsKey("Content-Encoding"))
                    {
                        var encoding = ifile.Headers["Content-Encoding"].ToString();
                        //Console.WriteLine($"/note/insertNoteFace 请求内容已被压缩，算法：{encoding}");

                        if ("gzip".Equals(encoding))
                        {
                            try
                            {
                                using MemoryStream compressedStream = new MemoryStream();
                                using MemoryStream decompressedStream = new MemoryStream();
                                await ifile.CopyToAsync(compressedStream);
                                compressedStream.Position = 0;
                                using var gzipStream = new GZipStream(compressedStream, CompressionMode.Decompress);

                                await gzipStream.CopyToAsync(decompressedStream);
                                decompressedStream.Position = 0;
                                using var requestReader = new StreamReader(decompressedStream, encoding: System.Text.Encoding.UTF8, leaveOpen: true);
                                recordJson = await requestReader.ReadToEndAsync();
                            }
                            catch (Exception)
                            {

                                recordJson = string.Empty;
                            }


                        }
                    }
                }
                if (string.IsNullOrEmpty(recordJson))
                {
                    Console.WriteLine("URL:{0} RemoteIP:{1}:{2}", HttpContext.Request.Path,
                       HttpContext.Connection.RemoteIpAddress,
                       HttpContext.Connection.RemotePort);
                    return new FaceAPIResult();
                }

            }

            //防止重复记录
            //string recordMD5 = MD5Helper.GetStringMD5(recordJson);
            //if(Cache.CheckReplayAttack(recordMD5))
            //    return new FaceAPIResult();
            APIModel_DeviceRecord record = JsonConvert.DeserializeObject<APIModel_DeviceRecord>(recordJson);
            var imageConfig = HttpContext.RequestServices.GetService<IOptionsMonitor<RecordImageConfig>>().CurrentValue;
            if (record.EmployeeID == -1) record.EmployeeID = 0;

            if (record.Pic_Len > 0 && files.Files.Count == 1)
            {
                using (var stream = new MemoryStream(record.Pic_Len))
                {
                    var uploadFile = files.Files[0];
                    string md5 = string.Empty;
                    await uploadFile.CopyToAsync(stream);
                    md5 = MD5Helper.GetStreamMD5ByHex(stream);

                    #region 获取文件名

                    var sFileNameFormat = imageConfig.NameFormat;
                    if (sFileNameFormat.Contains("{SN}"))
                        sFileNameFormat = sFileNameFormat.Replace("{SN}", record.DeviceID);
                    if (sFileNameFormat.Contains("{yyyyMM}"))
                        sFileNameFormat = sFileNameFormat.Replace("{yyyyMM}", record.NoteTime.ToString("yyyyMM"));
                    if (sFileNameFormat.Contains("{yyyyMMdd}"))
                        sFileNameFormat = sFileNameFormat.Replace("{yyyyMMdd}", record.NoteTime.ToString("yyyyMMdd"));
                    if (sFileNameFormat.Contains("{dd}"))
                        sFileNameFormat = sFileNameFormat.Replace("{dd}", record.NoteTime.ToString("dd"));
                    if (sFileNameFormat.Contains("{HHmm}"))
                        sFileNameFormat = sFileNameFormat.Replace("{HHmm}", record.NoteTime.ToString("HHmm"));
                    //拆除最后一个文件名节点  {YYYY_MM_dd_HH_mm_ss_PCode_PName.jpg}
                    var imageName = sFileNameFormat.Substring(sFileNameFormat.LastIndexOf('/') + 1);
                    var imageNameRule = imageName.Substring(imageName.IndexOf('{'), imageName.IndexOf('}') + 1);
                    var imageNameRuleBlack = imageNameRule;
                    //时间规则
                    if (imageNameRule.Contains("yyyy_MM_dd_HH_mm_ss"))
                        imageNameRule = imageNameRule.Replace("yyyy_MM_dd_HH_mm_ss", record.NoteTime.ToString("yyyy_MM_dd_HH_mm_ss"));
                    if (imageNameRule.Contains("yyyy_MM_dd"))
                        imageNameRule = imageNameRule.Replace("yyyy_MM_dd", record.NoteTime.ToString("yyyy_MM_dd"));
                    if (imageNameRule.Contains("HH_mm_ss"))
                        imageNameRule = imageNameRule.Replace("HH_mm_ss", record.NoteTime.ToString("HH_mm_ss"));
                    if (imageNameRule.Contains("yyyyMMddHHmmss"))
                        imageNameRule = imageNameRule.Replace("yyyyMMddHHmmss", record.NoteTime.ToString("yyyyMMddHHmmss"));
                    if (imageNameRule.Contains("yyyyMMdd"))
                        imageNameRule = imageNameRule.Replace("yyyyMMdd", record.NoteTime.ToString("yyyyMMdd"));
                    if (imageNameRule.Contains("HHmmss"))
                        imageNameRule = imageNameRule.Replace("HHmmss", record.NoteTime.ToString("HHmmss"));
                    imageNameRule = imageNameRule.Substring(1, imageNameRule.Length - 2);
                    var imageNameChilds = imageNameRule.Split('_');
                    for (int i = 0; i < imageNameChilds.Length; i++)
                    {
                        var rule = imageNameChilds[i];
                        switch (rule)
                        {
                            case "yyyy":
                                imageNameChilds[i] = record.NoteTime.ToString("yyyy");
                                break;
                            case "MM":
                                imageNameChilds[i] = record.NoteTime.ToString("MM");
                                break;
                            case "dd":
                                imageNameChilds[i] = record.NoteTime.ToString("dd");
                                break;
                            case "HH":
                                imageNameChilds[i] = record.NoteTime.ToString("HH");
                                break;
                            case "mm":
                                imageNameChilds[i] = record.NoteTime.ToString("mm");
                                break;
                            case "ss":
                                imageNameChilds[i] = record.NoteTime.ToString("ss");
                                break;
                            case "UserCode":
                                imageNameChilds[i] = record.EmployeeID.ToString("D10");
                                break;
                            case "UserName":
                                imageNameChilds[i] = record.EmployeeName;
                                break;
                            case "GUID":
                                imageNameChilds[i] = Guid.NewGuid().ToString("N");
                                break;
                            case "MD5":
                                imageNameChilds[i] = md5;
                                break;
                            default:
                                break;
                        }
                    }
                    imageNameRule = string.Join('_', imageNameChilds);
                    sFileNameFormat = sFileNameFormat.Replace(imageNameRuleBlack, imageNameRule);
                    #endregion
                    string sFile = Path.Combine(Directory.GetCurrentDirectory(),
                        imageConfig.Root, sFileNameFormat);
                    var sDir = Path.GetDirectoryName(sFile);
                    if (!Directory.Exists(sDir))
                    {
                        Directory.CreateDirectory(sDir);
                    }


                    if (uploadFile.Length > 0)
                    {
                        record.Pic_Len = (int)uploadFile.Length;
                        stream.Position = 0;
                        using (var filestream = new FileStream(sFile, FileMode.Create))
                        {
                            await stream.CopyToAsync(filestream);
                        }
                        record.ImgURL = imageConfig.URLPrefix + sFileNameFormat;
                    }
                    Logger.LogInformation($"上传打卡记录，记录照片：{record.ImgURL}，代码：{record.EventType}，时间：{record.NoteTime}");
                }
            }
            else
            {
                record.Pic_Len = 0;
                record.ImgURL = string.Empty;
                Logger.LogInformation($"上传打卡记录，无记录照片，代码：{record.EventType}，时间：{record.NoteTime}");
            }

            DBService.AddRecord(record);

            return new FaceAPIResult() { msg = record.ImgURL };
        }
        #endregion

        #region 保活包
        /// <summary>
        /// 心跳保活包
        /// </summary>
        [HttpPost, Route("/device/updateStateDevice")]
        public async Task<DeviceKeepaliveResult> DeviceKeepalive([FromBody] DeviceKeepaliveRequest par)
        {
            var result = new DeviceKeepaliveResult();

            var SN = par.DeviceID;
            if (string.IsNullOrWhiteSpace(SN))
            {
                Logger.LogError($"心跳保活包 不处理 SN:{SN} SN为空");
                return result;//不处理
            }
            else
            {
                if (SN.Length < 16 || SN == "0000000000000000")
                {
                    Logger.LogError($"心跳保活包 不处理 SN:{SN} 长度不合规或内容错误");
                    return result;//不处理
                }
                else
                {

                    #region 检查参数是否需要同步
                    var oDoorCache = Cache.GetDoor(SN);
                    #endregion

                    if (oDoorCache != null)
                    {
                        if (oDoorCache.WorkParameter == null)
                        {
                            Logger.LogInformation($"{SN} 心跳保活包  第一次连接，要求上传参数");
                            //需要先上传参数
                            result.UploadWorkParameter = 1;//上传参数并注册
                        }
                        else
                        {
                            #region 检查权限是否需要同步
                            string log = string.Empty;
                            result.DeletePeople = oDoorCache.DeleteAccessTotal;
                            if (oDoorCache.DeleteAccessTotal == 0 && oDoorCache.EmptyPeople != 0)
                            {
                                result.DeletePeople = 1;
                            }
                            result.AddPeople = oDoorCache.NewAccessTotal;
                            result.Remote = oDoorCache.RemoteTaskTotal;

                            if (result.AddPeople > 0)
                            {
                                log = $"要求设备拉取人员，待拉取数量：{result.AddPeople}，";
                            }
                            if (result.DeletePeople > 0)
                            {
                                log += $"要求设备删除人员，待删除数量：{result.DeletePeople}";
                            }
                            #endregion

                            //oDoor = _DriveDB.Find<FaceDeviceDetail>(oDoor.ID);
                            oDoorCache.LastKeepaliveTime = DateTime.Now;
                            oDoorCache.OnlineTime = DateTime.Now;
                            oDoorCache.AlarmStatus = par.Alarm;
                            oDoorCache.DoorStatus = par.Door;
                            if (oDoorCache.UploadStatus == 0)
                            {
                                result.SyncParameter = 1;
                            }

                            Logger.LogDebug($"{SN} 心跳保活包 {log}");

                            await DBService.UpdateDoorUploadStatus(oDoorCache);
                        }

                    }
                    else
                    {
                        Console.WriteLine("保活包，未注册，要求上传参数");
                        result.UploadWorkParameter = 1;//上传参数并注册
                    }
                }


            }

            return result;
        }
        #endregion

        #region 检查设备是否已注册
        /// <summary>
        /// 检查设备是否已注册
        /// </summary>
        /// <param name="SN"></param>
        /// <param name="oDoor"></param>
        /// <returns></returns>
        private FaceAPIResult CheckDeviceReg(string SN, out DTOCacheDoor oDoor)
        {
            FaceAPIResult rst = new();
            oDoor = null;
            if (string.IsNullOrWhiteSpace(SN))
            {
                rst.success = 1;
                rst.msg = "SN is error！";
                return rst;
            }

            if (SN.Length < 16 || SN == "0000000000000000")
            {
                rst.success = 1;
                rst.msg = "SN is error！";
                return rst;
            }

            oDoor = Cache.GetDoor(SN);
            if (oDoor == null)
            {
                rst.success = 1;
                rst.msg = "SN is unregistered!";
                return rst;
            }
            //更新连接时间
            oDoor.OnlineTime = DateTime.Now;

            return null;
        }
        #endregion

        #region 人员操作
        /// <summary>
        ///  添加人员 设备主动触发云端导入雇员授权信息
        /// </summary>
        [HttpPost, Route("/devicePass/selectPassInfo")]
        public async Task<FaceAPIResult> ReadPeople(ReadPeopleRequestRequest par)
        {


            ReadPeopleResult rst = new();
            DTOCacheDoor oDoor = null;
            string SN = par.DeviceID;
            Logger.LogInformation($" 设备拉取人事信息 ReadPeople SN:{SN}");
            var chkRet = CheckDeviceReg(SN, out oDoor);
            if (chkRet != null) return chkRet;

            if (par.Limit == 0) par.Limit = 50;
            if (par.Limit > 100) par.Limit = 100;
            //检查有没有需要添加的人员
            var TotalDetail = oDoor.NewAccessTotal;

            if (TotalDetail == 0)
            {
                //没有需要上传的人员
                rst.success = 2;
                rst.msg = "no people";//  "没有需要同步的人员!";
                Logger.LogWarning($"{SN} 拉取人员 ，没有需要同步的人员");
                return rst;
            }
            //从数据库中获取需要导入的人员
            rst.PassInfo = await DBService.GetDoorAccess(oDoor, par.Limit);
            if (rst.PassInfo == null)
            {
                rst.success = 2;
                rst.msg = "no people";//  "没有需要同步的人员!";
                Logger.LogWarning($"{SN} 拉取人员 ，没有需要同步的人员 数据库查询待更新人数为0");
                return rst;
            }
            else
            {
                //StringBuilder sbuf = new StringBuilder(rst.PassInfo.Count * 50);
                //foreach (var item in rst.PassInfo)
                //{
                //    sbuf.Append(item.EmployeeName).AppendFormat("({0})", item.EmployeeID)
                //        .Append("，有效期：").Append(item.DevicePassBean.DevicePassEnd.ToString("yyyy-MM-dd HH:mm:ss"))
                //        .Append("，有效次数：").Append(item.DevicePassBean.DevicePassNumber)
                //        .Append("，开门时段：").Append(item.TimegroupID)
                //        .AppendLine();
                //}


                Logger.LogInformation($"{SN} 拉取人员 返回数量：{rst.PassInfo.Count}");
                //Logger.LogInformation(Newtonsoft.Json.JsonConvert.SerializeObject(rst.PassInfo, Formatting.Indented));
            }

            return rst;
        }

        /// <summary>
        /// 添加人员反馈  拉取人员导入人脸机后的结果反馈
        /// </summary>
        [HttpPost, Route("/devicePass/setPassResult")]
        public async Task<FaceAPIResult> ReadPeopleResult([FromBody] DeviceReadPeopleResultRequest par)
        {
            //_logger.LogInformation($" 设备推送人事保存结果 ReadPeopleResult SN:{par.DeviceID}");
            DTOCacheDoor oDoor = null;
            string SN = par.DeviceID;
            var chkRet = CheckDeviceReg(SN, out oDoor);
            if (chkRet != null) return chkRet;

            StringBuilder sbuf = new StringBuilder((par.FailNumber + 1) * 25);
            if (par.FailNumber > 0)
            {
                sbuf.Append("有上传失败的人员：").AppendLine();
                foreach (var item in par.FailEmployeeId)
                {
                    sbuf.Append(item.EmployeeID).Append("，错误代码：").Append(item.ErrorCode);
                    if (item.RepeatID > 0)
                    {
                        sbuf.Append("，重复人员编号：").Append(item.RepeatID);
                    }
                    sbuf.AppendLine();
                }

            }

            Logger.LogInformation($"{SN} 拉取人员，存储完毕 \r\n {sbuf}");
            await DBService.SaveDoorAccessUploadResult(oDoor, par.FailNumber, par.FailEmployeeId);

            return new FaceAPIResult();
        }



        /// <summary>
        /// 删除人员   拉取待删除的人员
        /// </summary>
        [HttpPost, Route("/devicePass/selectDeleteInfo")]
        public async Task<FaceAPIResult> DeletePeople([FromBody] ReadPeopleRequestRequest par)
        {
            //_logger.LogInformation($" 设备拉取待删除人员 DeletePeople SN:{par.DeviceID}");

            DeletePeopleResult rst = new();
            DTOCacheDoor oDoor = null;
            var SN = par.DeviceID;
            var chkRet = CheckDeviceReg(SN, out oDoor);
            if (chkRet != null) return chkRet;




            //检查有没有需要删除的人员

            if (oDoor.DeleteAccessTotal == 0 && oDoor.EmptyPeople == 0)
            {
                //没有需要上传的人员
                rst.success = 2;
                rst.msg = "no people";//"没有需要删除的人员!";
                Logger.LogWarning($"{SN} 删除人员 ，没有需要删除的人员");
                return rst;
            }

            if (oDoor.AccessTotal == 0 || oDoor.EmptyPeople > 0)
            {
                //需要删除所有人员
                rst.Empty = 1;
                rst.success = 0;
                rst.msg = "delete all";// "删除所有人员";
                Logger.LogWarning($"{SN} 删除人员 需要删除所有人员");
                //_AccessDB.AddUserLog(_LanguageHandler.GetUserLog("t1"),// "权限管理",
                //                     string.Format(_LanguageHandler.GetUserLog("r1"), SN)); //$"设备SN:{par.DeviceID} ，请求清空人员命令");
                return rst;
            }

            if (par.Limit == 0) par.Limit = 50;
            if (par.Limit > 100) par.Limit = 100;

            //从数据库中获取需要删除的人员
            rst.DeleteInfo = await DBService.GetDoorAccessByDelete(oDoor, par.Limit);
            if (rst.DeleteInfo.Count == 0)
            {
                rst.success = 2;
                rst.msg = "no people";//"没有需要删除的人员!";
                Logger.LogWarning($"{SN} 删除人员 ，没有需要删除的人员 数据库查询结果为0");
                return rst;
            }
            else
            {
                StringBuilder sbuf = new StringBuilder(rst.DeleteInfo.Count * 12);
                foreach (var item in rst.DeleteInfo)
                {
                    sbuf.Append(item.EmployeeID).AppendLine();
                }


                Logger.LogInformation($"{SN} 删除人员，人员编号列表： \r\n {sbuf}");
            }
            return rst;
        }

        /// <summary>
        /// 删除人员反馈   设备从服务器拉取的待删除的人员操作后的结果反馈
        /// </summary>
        [HttpPost, Route("/devicePass/setDeleteResult")]
        public async Task<FaceAPIResult> ReadDeletePeople([FromBody] DeviceDeletePeopleResultRequest par)
        {
            var SN = par.DeviceID;
            var chkRet = CheckDeviceReg(SN, out DTOCacheDoor oDoor);
            if (chkRet != null) return chkRet;
            Logger.LogInformation($"{SN} 删除人员，删除成功反馈");
            await DBService.SaveDoorAccessDeleteResult(oDoor, par.DeleteAll);

            return new FaceAPIResult();
        }
        #endregion

        #region 属性配置
        /// <summary>
        ///  上传参数 设备将机器现有配置上传服务器
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpPost, Route("/parameter/inertParameter")]
        public async Task<FaceAPIResult> UploadWorkParameter([FromBody] APIModel_WorkParameter par)
        {
            //_logger.LogInformation($" 设备上传参数 parameter/inertParameter SN:{SN}");
            var SN = par.DeviceID;
            if (string.IsNullOrWhiteSpace(SN))
            {
                Logger.LogError($"{SN} 设备上传参数 没有SN 不处理");
                return new FaceAPIResult();//没有SN不处理
            }

            if (SN.Length < 15 || SN == "0000000000000000")
            {
                Logger.LogError($"{SN} 设备上传参数 SN不符合规则 不处理");
                return new FaceAPIResult();//没有SN不处理
            }
            Logger.LogInformation($"{SN} 设备上传参数");
            await DBService.SaveDoorWorkParameter(par);

            return new FaceAPIResult();
        }

        /// <summary>
        ///  拉取参数 设备请求同步参数，将服务器存储内容发送到设备
        /// </summary>
        /// <param name="par"></param>
        /// <returns></returns>
        [HttpPost, Route("/parameter/selectParameterInfo")]
        public async Task<FaceAPIResult> GetWorkParameter([FromBody] BaseDeviceRequest par)
        {

            var SN = par.DeviceID;

            if (string.IsNullOrWhiteSpace(par.DeviceID))
            {
                Logger.LogError($"{SN} 设备拉取参数 SN为空 不处理");
                return new FaceAPIResult() { success = 1, msg = "SN is error" };

            }
            else
            {
                if (par.DeviceID.Length < 15 || par.DeviceID == "0000000000000000")
                {
                    Logger.LogError($"{SN} 设备拉取参数 SN不符合规则 不处理");
                    return new FaceAPIResult() { success = -1, msg = "SN is error" };
                }
                else
                {
                    var chkRet = CheckDeviceReg(par.DeviceID, out DTOCacheDoor oCacheDevice);
                    if (chkRet != null) return chkRet;

                    GetWorkParameterResult result = await DBService.GetDoorWorkParameter(oCacheDevice);
                    if (result == null)
                    {
                        Logger.LogError($"{SN} 设备拉取参数 获取参数失败");
                        return new FaceAPIResult() { success = -1, msg = "SN is error" };
                    }
                    Logger.LogInformation($" 设备拉取参数 SN:{oCacheDevice.EquptSN}  {oCacheDevice.EquptName}");
                    //if (!string.IsNullOrEmpty(oCacheDevice.UpdateSoftMD5))
                    //{
                    //    detail.parameterFirmwareMd5 = oCacheDevice.UpdateSoftMD5;
                    //    detail.parameterFirmwarePath = oCacheDevice.UpdateSoftURL;
                    //    detail.ParameterFirmwareMsg = oCacheDevice.UpdateSoftVer;
                    //    oCacheDevice.UpdateSoftMD5 = string.Empty;
                    //}
                    oCacheDevice.UploadStatus = 1;
                    oCacheDevice.UploadStatusTime = DateTime.Now;

                    await DBService.UpdateDoorUploadStatus(oCacheDevice);

                    result.ParameterSystemTime = TimestampUtility.ToUnixTimestampBySeconds(DateTime.Now);
                    result.Timegroups = await DBService.GetTimeGroup();

                    return result;
                }
            }

        }


    }
    #endregion

}



